package com.cauli.file.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.net.URLDecoder;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cauli.file.component.FileComponent;
import com.cauli.file.component.FileDealComp;
import com.cauli.file.mapper.RecoveryFileMapper;
import com.cauli.file.mapper.UserFileMapper;
import com.cauli.file.model.dto.file.FileQueryDTO;
import com.cauli.file.model.dto.file.UserFileDTO;
import com.cauli.file.model.entity.RecoveryFile;
import com.cauli.file.model.entity.UserFile;
import com.cauli.file.model.vo.file.FileListVO;
import com.cauli.file.service.UserFileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * @author Cauli
 * @date 2023-03-20 12:00
 * @description 用户文件 服务实现类
 */
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class UserFileServiceImpl extends ServiceImpl<UserFileMapper, UserFile> implements UserFileService {
    @Autowired
    private RecoveryFileMapper recoveryFileMapper;

    @Resource
    private FileDealComp fileDealComp;

    public static Executor executor = Executors.newFixedThreadPool(20);


    @Override
    public List<UserFile> selectUserFileByNameAndPath(String fileName, String filePath, Long userId) {
        LambdaQueryWrapper<UserFile> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(UserFile::getFileName, fileName)
                .eq(UserFile::getFilePath, filePath)
                .eq(UserFile::getUserId, userId)
                .eq(UserFile::getDeleted, 0)
                .eq(UserFile::getDeleteFlag, 0);
        return baseMapper.selectList(lambdaQueryWrapper);
    }

    @Override
    public List<UserFile> selectSameUserFile(String fileName, String filePath, String extendName, Long userId) {
        LambdaQueryWrapper<UserFile> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(UserFile::getFileName, fileName)
                .eq(UserFile::getFilePath, filePath)
                .eq(UserFile::getUserId, userId)
                .eq(UserFile::getExtendName, extendName)
                .eq(UserFile::getDeleted, 0)
                .eq(UserFile::getDeleteFlag, 0);
        return baseMapper.selectList(lambdaQueryWrapper);
    }

    @Override
    public IPage<FileListVO> userFileList(FileQueryDTO fileQueryDTO) {
        Page<FileListVO> page = new Page<>(fileQueryDTO.getPageNum(), fileQueryDTO.getPageSize());
        UserFileDTO userFileDTO = new UserFileDTO();
        userFileDTO.setUserId(StpUtil.getLoginIdAsLong());
        userFileDTO.setFilePath(URLDecoder.decodeForPath(fileQueryDTO.getFilePath(), StandardCharsets.UTF_8));
        return baseMapper.selectPageVo(page, userFileDTO, null, fileQueryDTO.getBeginTime(), fileQueryDTO.getEndTime());
    }

    @Override
    public void updateFilepathByUserFileId(Long userFileId, String newFilePath, Long userId) {
        UserFile userFile = baseMapper.selectById(userFileId);
        String oldFilePath = userFile.getFilePath();
        String fileName = userFile.getFileName();

        userFile.setFilePath(newFilePath);
        if (userFile.getIsDir() == 0) {
            String repeatFileName = fileDealComp.getRepeatFileName(userFile, userFile.getFilePath());
            userFile.setFileName(repeatFileName);
        }
        try {
            baseMapper.updateById(userFile);
        } catch (Exception e) {
            log.warn(e.getMessage());
        }
        // 移动子目录
        oldFilePath = new FileComponent(oldFilePath, fileName, true).getPath();
        newFilePath = new FileComponent(newFilePath, fileName, true).getPath();
        // 如果是目录，则需要移动子目录
        if (userFile.isDirectory()) {
            List<UserFile> list = selectUserFileByLikeRightFilePath(oldFilePath, userId);

            for (UserFile newUserFile : list) {
                newUserFile.setFilePath(newUserFile.getFilePath().replaceFirst(oldFilePath, newFilePath));
                if (newUserFile.getIsDir() == 0) {
                    String repeatFileName = fileDealComp.getRepeatFileName(newUserFile, newUserFile.getFilePath());
                    newUserFile.setFileName(repeatFileName);
                }
                try {
                    baseMapper.updateById(newUserFile);
                } catch (Exception e) {
                    log.warn(e.getMessage());
                }
            }
        }
    }

    @Override
    public void userFileCopy(Long userId, Long userFileId, String newFilePath) {
        UserFile userFile = baseMapper.selectById(userFileId);
        String oldFilePath = userFile.getFilePath();
        Long oldUserId = userFile.getUserId();
        String fileName = userFile.getFileName();

        userFile.setUserFileId(null);
        userFile.setFilePath(newFilePath);
        userFile.setUserId(userId);
        if (userFile.getIsDir() == 0) {
            String repeatFileName = fileDealComp.getRepeatFileName(userFile, userFile.getFilePath());
            userFile.setFileName(repeatFileName);
        }
        try {
            baseMapper.insert(userFile);
        } catch (Exception e) {
            log.warn(e.getMessage());
        }

        oldFilePath = new FileComponent(oldFilePath, fileName, true).getPath();
        newFilePath = new FileComponent(newFilePath, fileName, true).getPath();

        if (userFile.isDirectory()) {
            List<UserFile> subUserFileList = baseMapper.selectUserFileByLikeRightFilePath(oldFilePath, oldUserId);

            for (UserFile newUserFile : subUserFileList) {
                newUserFile.setFilePath(newUserFile.getFilePath().replaceFirst(oldFilePath, newFilePath));
                if (newUserFile.isDirectory()) {
                    String repeatFileName = fileDealComp.getRepeatFileName(newUserFile, newUserFile.getFilePath());
                    newUserFile.setFileName(repeatFileName);
                }
                newUserFile.setUserId(userId);
                try {
                    baseMapper.insert(newUserFile);
                } catch (Exception e) {
                    log.warn(e.getMessage());
                }
            }
        }
    }

    @Override
    public IPage<FileListVO> getFileByFileType(Long userId, FileQueryDTO fileQueryDTO) {
        Page<FileListVO> page = new Page<>(fileQueryDTO.getPageNum(), fileQueryDTO.getPageSize());
        UserFileDTO userFileDTO = new UserFileDTO();
        userFileDTO.setUserId(userId);
        return baseMapper.selectPageVo(page, userFileDTO, fileQueryDTO.getFileType(), fileQueryDTO.getBeginTime(), fileQueryDTO.getEndTime());
    }

    @Override
    public List<UserFile> selectUserFileListByPath(String filePath, Long userId) {
        LambdaQueryWrapper<UserFile> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper
                .eq(UserFile::getFilePath, filePath)
                .eq(UserFile::getUserId, userId)
                .eq(UserFile::getDeleted, 0)
                .eq(UserFile::getDeleteFlag, 0);
        return baseMapper.selectList(lambdaQueryWrapper);
    }

    @Override
    public List<UserFile> selectFilePathTreeByUserId(Long userId) {
        LambdaQueryWrapper<UserFile> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(UserFile::getUserId, userId)
                .eq(UserFile::getIsDir, 1)
                .eq(UserFile::getDeleted, 0)
                .eq(UserFile::getDeleteFlag, 0);
        return baseMapper.selectList(lambdaQueryWrapper);
    }

    @Override
    public void deleteUserFile(Long userFileId, Long userId) {
        UserFile userFile = baseMapper.selectById(userFileId);
        String uuid = UUID.randomUUID().toString();
        if (userFile.getIsDir() == 1) {
            LambdaUpdateWrapper<UserFile> userFileLambdaUpdateWrapper = new LambdaUpdateWrapper<UserFile>();
            userFileLambdaUpdateWrapper
                    .set(UserFile::getDeleteFlag, 1)
                    .set(UserFile::getDeleteBatchNum, uuid)
                    .set(UserFile::getDeleteTime, DateUtil.now())
                    .eq(UserFile::getUserFileId, userFileId);
            baseMapper.update(null, userFileLambdaUpdateWrapper);

            String filePath = new FileComponent(userFile.getFilePath(), userFile.getFileName(), true).getPath();
            this.updateFileDeleteStateByFilePath(filePath, uuid, userId);

        } else {
            UserFile userFileTemp = baseMapper.selectById(userFileId);
            LambdaUpdateWrapper<UserFile> userFileLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
            userFileLambdaUpdateWrapper
                    .set(UserFile::getDeleteFlag, 1)
                    .set(UserFile::getDeleteTime, DateUtil.now())
                    .set(UserFile::getDeleteBatchNum, uuid)
                    .eq(UserFile::getUserFileId, userFileTemp.getUserFileId());
            baseMapper.update(null, userFileLambdaUpdateWrapper);
        }

        RecoveryFile recoveryFile = new RecoveryFile();
        recoveryFile.setUserFileId(userFileId);
        recoveryFile.setDeleteTime(DateUtil.now());
        recoveryFile.setDeleteBatchNum(uuid);
        recoveryFileMapper.insert(recoveryFile);
    }

    @Override
    public List<UserFile> selectUserFileByLikeRightFilePath(String filePath, Long userId) {
        return baseMapper.selectUserFileByLikeRightFilePath(filePath, userId);
    }

    private void updateFileDeleteStateByFilePath(String filePath, String deleteBatchNum, Long userId) {
        executor.execute(() -> {
            List<UserFile> fileList = selectUserFileByLikeRightFilePath(filePath, userId);
            for (int i = 0; i < fileList.size(); i++) {
                UserFile userFileTemp = fileList.get(i);
                // 标记删除标志
                LambdaUpdateWrapper<UserFile> userFileLambdaUpdateWrapper1 = new LambdaUpdateWrapper<>();
                userFileLambdaUpdateWrapper1
                        .set(UserFile::getDeleteFlag, 1)
                        .set(UserFile::getDeleteTime, DateUtil.now())
                        .set(UserFile::getDeleteBatchNum, deleteBatchNum)
                        .eq(UserFile::getUserFileId, userFileTemp.getUserFileId())
                        .eq(UserFile::getDeleted, 0)
                        .eq(UserFile::getDeleteFlag, 0);
                baseMapper.update(null, userFileLambdaUpdateWrapper1);
            }
        });
    }
}
